package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.BreakPeriod;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.Shift;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.ShiftType;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseIdReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseListResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.shift.*;
import com.zmj.sy.mom.srv.aps.mapper.BreakPeriodMapper;
import com.zmj.sy.mom.srv.aps.mapper.ShiftMapper;
import com.zmj.sy.mom.srv.aps.mapper.ShiftTypeMapper;
import com.zmj.sy.mom.srv.aps.utils.MkBeanUtils;
import com.zmj.sy.mom.srv.aps.utils.SyExceptionUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class ShiftService {

    private final ShiftMapper shiftMapper;

    private final ShiftTypeMapper shiftTypeMapper;

    private final BreakPeriodMapper breakPeriodMapper;

    @Transactional
    public void add(ShiftAddReqVo reqVo) {
        ShiftType shiftType = shiftTypeMapper.selectById(reqVo.getShiftTypeId());
        if (shiftType == null) {
            throw SyExceptionUtils.e("班次类型不存在");
        }

        List<Shift> shifts = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().eq(Shift::getShiftTypeId, reqVo.getShiftTypeId()));

        Shift shift = MkBeanUtils.copyProperties(reqVo, Shift.class);
        shift.setSeq(shifts.size() + 1);
        shifts.add(shift);
        List<BreakPeriod> breakPeriods = reqVo.getBreakPeriods().stream().filter(e -> e.getStartTime() != null && e.getEndTime() != null).map(e -> MkBeanUtils.copyProperties(e, BreakPeriod.class)).collect(Collectors.toList());
        validTime(shifts, shift, breakPeriods);

        shiftMapper.insert(shift);
        int i = 1;
        for (Shift shift1 : shifts) {
            shift1.setSeq(i++);
            shiftMapper.updateById(shift1);
        }
        for (BreakPeriod breakPeriod : breakPeriods) {
            breakPeriod.setShiftId(shift.getId());
            breakPeriodMapper.insert(breakPeriod);
        }

//        // 校验规则
//        List<WeekdayRule> rules = reqVo.getRules().stream().map(e -> MkBeanUtils.copyProperties(e, WeekdayRule.class)).collect(Collectors.toList());
//        for (WeekdayRule rule : rules) {
//            weekdayRuleMapper.insert(rule);
//        }

    }

    public void validTime(List<Shift> shifts, Shift current, List<BreakPeriod> breakPeriods) {

        if(breakPeriods.size()==1){
            breakPeriods = breakPeriods.stream().filter(e -> !(e.getStartTime() == null || e.getEndTime() == null)).collect(Collectors.toList());
        }

        LocalDate now = LocalDate.now();
        LocalDateTime start = LocalDateTime.of(now.plusDays(current.getOffsetStartDay()), current.getStartTime());
        LocalDateTime end = LocalDateTime.of(now.plusDays(current.getOffsetEndDay()), current.getEndTime());
        if (start.until(end, ChronoUnit.MINUTES) <= 0) {
            throw SyExceptionUtils.e("班次的结束时间应大于开始时间");
        }

        if (start.until(end, ChronoUnit.MINUTES) > 60 * 24) {
            throw SyExceptionUtils.e("班次的总时长在24小时内");
        }
        shifts.sort(Comparator.comparing(e->LocalDateTime.of(now.plusDays(current.getOffsetStartDay()), e.getStartTime())));

        LocalDateTime min = shifts.stream().map(e->LocalDateTime.of(now.plusDays(e.getOffsetStartDay()), e.getStartTime())).min(Comparator.comparing(e->e)).orElseThrow(()->SyExceptionUtils.e("最小开始时间为空"));
        LocalDateTime max = shifts.stream().map(e->LocalDateTime.of(now.plusDays(e.getOffsetEndDay()), e.getEndTime())).min(Comparator.comparing(e->e)).orElseThrow(()->SyExceptionUtils.e("最大开始时间为空"));

        if (min.until(max, ChronoUnit.MINUTES) < 0) {
            throw SyExceptionUtils.e("班制的开始时间段应小于结束时间");
        }

        if (min.until(max, ChronoUnit.MINUTES) > 60 * 24) {
            throw SyExceptionUtils.e("班制的总时长在24小时内");
        }

        int totalBreakTime = 0;
        if (CollUtil.isEmpty(breakPeriods)) {
            current.setTotalTime((int) start.until(end, ChronoUnit.MINUTES));
            current.setTotalBreakTime(totalBreakTime);
            current.setTotalWorkTime(current.getTotalTime() - current.getTotalBreakTime());
            return;
        }
        // 校验休息时间段是否合法
        min = breakPeriods.stream().map(e->LocalDateTime.of(now.plusDays(e.getOffsetStartDay()), e.getStartTime())).min(Comparator.comparing(e->e)).orElseThrow(()->SyExceptionUtils.e("休息的最小开始时间为空"));
        max = breakPeriods.stream().map(e->LocalDateTime.of(now.plusDays(e.getOffsetEndDay()), e.getEndTime())).min(Comparator.comparing(e->e)).orElseThrow(()->SyExceptionUtils.e("休息的最大开始时间为空"));
        for (int i = 0; i < breakPeriods.size(); i++) {

            if (breakPeriods.get(i).getOffsetStartDay() == null || breakPeriods.get(i).getStartTime() == null || breakPeriods.get(i).getOffsetEndDay() == null || breakPeriods.get(i).getEndTime() == null) {
                throw SyExceptionUtils.e("请填写所有值");
            }

            if (i == 0) {
                min = LocalDateTime.of(now.plusDays(breakPeriods.get(i).getOffsetStartDay()), breakPeriods.get(i).getStartTime());
            }

            if (i == breakPeriods.size() - 1) {
                max = LocalDateTime.of(now.plusDays(breakPeriods.get(i).getOffsetEndDay()), breakPeriods.get(i).getEndTime());
            }

            // 判断开始时间是否大于上一次的结束时间
            if (i > 0) {
                LocalDateTime start1 = LocalDateTime.of(now.plusDays(breakPeriods.get(i).getOffsetStartDay()), breakPeriods.get(i).getStartTime());
                LocalDateTime end1 = LocalDateTime.of(now.plusDays(breakPeriods.get(i - 1).getOffsetEndDay()), breakPeriods.get(i - 1).getEndTime());
                if (end1.until(start1, ChronoUnit.MINUTES) < 0) {
                    throw SyExceptionUtils.e("休息的开始时间段应大于等于上次结束时间");
                }
            }

            // 判断此结束时间是否大于开始时间
            LocalDateTime start1 = LocalDateTime.of(now.plusDays(breakPeriods.get(i).getOffsetStartDay()), breakPeriods.get(i).getStartTime());
            LocalDateTime end1 = LocalDateTime.of(now.plusDays(breakPeriods.get(i).getOffsetEndDay()), breakPeriods.get(i).getEndTime());
            if (start1.until(end1, ChronoUnit.MINUTES) <= 0) {
                throw SyExceptionUtils.e("休息的结束时间应该大于开始时间");
            }

            totalBreakTime = (int)start1.until(end1, ChronoUnit.MINUTES);
        }

        if (min.until(max, ChronoUnit.MINUTES) < 0) {
            throw SyExceptionUtils.e("休息的开始时间不能在工作时间之外");
        }

        current.setTotalTime((int) start.until(end, ChronoUnit.MINUTES));
        current.setTotalBreakTime(totalBreakTime);
        current.setTotalWorkTime(current.getTotalTime() - current.getTotalBreakTime());

    }

    public BaseListResVo<ShiftListResVo> list(ShiftListReqVo reqVo) {

        List<Shift> shiftPage = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().eq(Shift::getShiftTypeId, reqVo.getShiftTypeId()));
        BaseListResVo<ShiftListResVo> resVo = BaseListResVo.of(shiftPage, ShiftListResVo.class);

        if(resVo.getData().isEmpty()){
            return resVo;
        }
        List<Integer> ids = resVo.getData().stream().map(ShiftListResVo::getId).collect(Collectors.toList());
        List<BreakPeriod> breakPeriods = breakPeriodMapper.selectList(Wrappers.<BreakPeriod>lambdaQuery().in(BreakPeriod::getShiftId, ids));
        Map<Integer, List<ShiftListBreakPeriodResVo>> collect = breakPeriods.stream().map(e->MkBeanUtils.copyProperties(e, ShiftListBreakPeriodResVo.class)).collect(Collectors.groupingBy(ShiftListBreakPeriodResVo::getShiftId));

        for (ShiftListResVo datum : resVo.getData()) {
            List<ShiftListBreakPeriodResVo> breakPeriods1 = collect.get(datum.getId());
            if(breakPeriods1 != null){
                datum.setBreakPeriods(breakPeriods1);
            } else {
                datum.setBreakPeriods(new ArrayList<>());
                ShiftListBreakPeriodResVo shiftListBreakPeriodResVo = new ShiftListBreakPeriodResVo();
                shiftListBreakPeriodResVo.setOffsetStartDay(0);
                shiftListBreakPeriodResVo.setStartTime(null);
                shiftListBreakPeriodResVo.setOffsetEndDay(1);
                shiftListBreakPeriodResVo.setEndTime(null);
                datum.getBreakPeriods().add(shiftListBreakPeriodResVo);
            }
        }

        return resVo;
    }

    @Transactional
    public void edit(ShiftEditReqVo reqVo) {


        ShiftType shiftType = shiftTypeMapper.selectById(reqVo.getShiftTypeId());
        if (shiftType == null) {
            throw SyExceptionUtils.e("班次类型不存在");
        }

        Shift shift = shiftMapper.selectById(reqVo.getId());
        if (shift == null) {
            throw SyExceptionUtils.e("班次不存在");
        }

        List<Shift> shifts = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().eq(Shift::getShiftTypeId, reqVo.getShiftTypeId()));
        shift = shifts.stream().filter(e -> e.getId().equals(reqVo.getId())).findFirst().orElseThrow(() -> SyExceptionUtils.e("班次不存在"));
        MkBeanUtils.copyPropertiesIgnore(reqVo, shift, "id", "shiftTypeId", "seq");
        List<BreakPeriod> breakPeriods = reqVo.getBreakPeriods().stream().filter(e -> e.getStartTime() != null && e.getEndTime() != null).map(e -> MkBeanUtils.copyProperties(e, BreakPeriod.class)).collect(Collectors.toList());
        validTime(shifts, shift, breakPeriods);

        for (Shift shift1 : shifts) {
            shiftMapper.updateById(shift1);
        }
        breakPeriodMapper.delete(Wrappers.<BreakPeriod>lambdaQuery().eq(BreakPeriod::getShiftId, shift.getId()));
        for (BreakPeriod breakPeriod : breakPeriods) {
            breakPeriod.setShiftId(shift.getId());
            breakPeriodMapper.insert(breakPeriod);
        }

    }

    @Transactional
    public void delete(BaseIdReqVo reqVo) {
        Shift shift = shiftMapper.selectById(reqVo.getId());
        List<Shift> shifts = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().eq(Shift::getShiftTypeId, shift.getShiftTypeId()).orderByAsc(Shift::getSeq));
        shifts.removeIf(e->e.getId().equals(reqVo.getId()));
        int i = 1;
        for (Shift shift1 : shifts) {
            shift1.setSeq(i++);
            shiftMapper.updateById(shift1);
        }

        shiftMapper.deleteById(reqVo.getId());

    }

    public ShiftGetResVo get(BaseIdReqVo reqVo) {
        Shift shift = shiftMapper.selectById(reqVo.getId());
        if (shift == null) {
            throw SyExceptionUtils.e("班次不存在");
        }

        ShiftGetResVo resVo = MkBeanUtils.copyProperties(shift, ShiftGetResVo.class);
        List<BreakPeriod> breakPeriods = breakPeriodMapper.selectList(Wrappers.<BreakPeriod>lambdaQuery().eq(BreakPeriod::getShiftId, shift.getId()));
        resVo.setBreakPeriods(breakPeriods.stream().map(e->MkBeanUtils.copyProperties(e, ShiftGetBreakPeriodResVo.class)).collect(Collectors.toList()));
        return resVo;
    }

    public BaseListResVo<ShiftListResVo> stationList(BaseIdReqVo reqVo) {
        List<Shift> shiftPage = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().eq(Shift::getShiftTypeId, reqVo.getId()));
        BaseListResVo<ShiftListResVo> resVo = BaseListResVo.of(shiftPage, ShiftListResVo.class);

        if(resVo.getData().isEmpty()){
            return resVo;
        }
        List<Integer> ids = resVo.getData().stream().map(ShiftListResVo::getId).collect(Collectors.toList());
        List<BreakPeriod> breakPeriods = breakPeriodMapper.selectList(Wrappers.<BreakPeriod>lambdaQuery().in(BreakPeriod::getShiftId, ids));
        Map<Integer, List<ShiftListBreakPeriodResVo>> collect = breakPeriods.stream().map(e->MkBeanUtils.copyProperties(e, ShiftListBreakPeriodResVo.class)).collect(Collectors.groupingBy(ShiftListBreakPeriodResVo::getShiftId));

        for (ShiftListResVo datum : resVo.getData()) {
            List<ShiftListBreakPeriodResVo> breakPeriods1 = collect.get(datum.getId());
            if(breakPeriods1 != null){
                datum.setBreakPeriods(breakPeriods1);
            } else {
                datum.setBreakPeriods(new ArrayList<>());
                ShiftListBreakPeriodResVo shiftListBreakPeriodResVo = new ShiftListBreakPeriodResVo();
                shiftListBreakPeriodResVo.setOffsetStartDay(0);
                shiftListBreakPeriodResVo.setStartTime(null);
                shiftListBreakPeriodResVo.setOffsetEndDay(1);
                shiftListBreakPeriodResVo.setEndTime(null);
                datum.getBreakPeriods().add(shiftListBreakPeriodResVo);
            }
        }

        return resVo;
    }
}
